package nl.boelen.game;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import nl.boelen.game.enums.GameState;
import nl.boelen.game.enums.ScreenState;
import nl.boelen.game.handler.KeyboardHandler;
import nl.boelen.game.handler.MouseHandler;
import nl.boelen.game.screen.GameScreen;
import nl.boelen.game.screen.MenuScreen;

public class Game extends JFrame implements Runnable {
    private static final int WIDTH = 800;
    private static final int HEIGHT = (WIDTH / 4) * 3;
    private static final Dimension DIM = new Dimension(WIDTH, HEIGHT);
    private static final String TITLE = "Hospital Builder";
    private static final double FRAMES_PER_SECOND = 60;

    private GameState gameState = GameState.PAUSED;
    private ScreenState screenState = ScreenState.MENU;

    private int framesLastSecond;
    private int ticksLastSecond;
    private KeyboardHandler keyboardHandler;
    private MouseHandler mouseHandler;
    private Thread gameThread;
    private GameScreen gameScreen;
    private MenuScreen menuScreen;

    public Game() {
        setTitle(TITLE);
        setMaximumSize(DIM);
        setMinimumSize(DIM);
        setPreferredSize(DIM);

        setUndecorated(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        setResizable(false);
        pack();

        // setLocationRelativeTo(null);
        setLocation(150, 90);
        setVisible(true);
    }

    public void init() {
        mouseHandler = new MouseHandler(this);
        keyboardHandler = new KeyboardHandler(this);

        gameScreen = new GameScreen(this, mouseHandler);
        add(gameScreen);
        menuScreen = new MenuScreen(this, mouseHandler);
        add(menuScreen);

        gameScreen.setVisible(false);
        menuScreen.setVisible(true);
    }

    public void start() {
        gameState = GameState.CREATING;
        gameThread = new Thread(this);
        gameThread.start();
    }

    @Override
    public void run() {
        init();

        /* Limit the rendering */
        long lastTime = System.nanoTime();
        long lastTimer = System.currentTimeMillis();
        double nsPerTick = 1000000000D / FRAMES_PER_SECOND;

        int ticks = 0;
        int frames = 0;

        double processedNs = 0;
        boolean shouldRender;

        for (;;) {
            long now = System.nanoTime();
            processedNs += (now - lastTime) / nsPerTick;
            lastTime = now;

            // False to stop overloading the system
            shouldRender = false;
            while (processedNs >= 1) {
                ticks++;
                tick();
                processedNs -= 1;
                shouldRender = true;
            }

            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (shouldRender) {
                frames++;
                render();
            }

            if (System.currentTimeMillis() - lastTimer >= 1000) {
                lastTimer += 1000;
                framesLastSecond = frames;
                frames = 0;
                ticksLastSecond = ticks;
                ticks = 0;
            }
        }
    }

    public void play() {
        screenState = ScreenState.GAME;
        gameState = GameState.RUNNING;
        gameScreen.setVisible(true);
        menuScreen.setVisible(false);
    }

    public void pause() {
        screenState = ScreenState.MENU;
        gameState = GameState.PAUSED;
        gameScreen.setVisible(false);
        menuScreen.setVisible(true);
        setTitle(TITLE);
    }

    public void resume() {
        screenState = ScreenState.GAME;
        gameState = GameState.RUNNING;
        gameScreen.setVisible(true);
        menuScreen.setVisible(false);
    }

    public void stop() {
        gameState = GameState.CREATING;
        menuScreen.render();
    }

    public void quit() {
        WindowEvent windowEvent = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
        Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(windowEvent);
    }

    public void tick() {
        if (screenState == ScreenState.GAME) {
            gameScreen.tick();
            if (keyboardHandler.pause.isPressed()) {
                pause();
            }
        }
    }

    public void render() {
        if (screenState == ScreenState.GAME) {
            gameScreen.render();
            setTitle(TITLE + " - frames:" + framesLastSecond + " ticks:" + ticksLastSecond);
        }
    }

    public GameState getGameState() {
        return gameState;
    }
}
